home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Power CD-ROM!! 8
/
Power CD-ROM 8.iso
/
prgmming
/
pmd110
/
memcheck.pas
< prev
next >
Wrap
Pascal/Delphi Source File
|
1994-11-13
|
10KB
|
383 lines
{ Created : 1993-04-25
Memory checker, checks for deallocating with a different size than the
allocated size and tracks not deallocated memory.
$Author$
$Date$
$Revision$
Last changes :
93-12-08 Adapted MemCheck to TDInfo
94-10-03 Extended width of error report
Added caller of caller to allocation item to make finding the
memory slip easier. The caller of th caller is shown in MEMCHECK.RPT
94-10-10 Installed exit handlers could cause other deallocations after MemCheck
called Halt (because when an error has occured). You could get a 204
in that case, so now MemCheck turns itself on, before calling Halt.
}
{$X+,O-,S-,R-,Q-,I-}
unit MemCheck;
interface
const
MemCheckDescr:string = ''; { not used yet }
const
ReportFileName = 'MEMCHECK.RPT';
procedure StoreAlloc(MemPtr : pointer; Size : word);
procedure FreeAlloc(MemPtr : pointer; Size : word);
procedure MemCheckReport;
implementation
uses Objects,
BBError, BBGui, BBUtil,
TDInfo;
type
PAllocItem = ^TAllocItem;
TAllocItem = record
MemPtr : pointer;
Caller,
CallerItsCaller : pointer;
Size : word;
end;
PAllocCollection = ^TAllocCollection;
TAllocCollection = object(TSortedCollection)
function Compare(Key1, Key2 : pointer) : integer; virtual;
procedure FreeItem(Item : pointer); virtual;
procedure Insert(Item : pointer); virtual;
function KeyOf(Item : pointer) : pointer; virtual;
end;
PMemCheckRec = ^TMemCheckRec;
TMemCheckRec = record
CheckMem : WordBool;
StoreAlloc : pointer;
FreeAlloc : pointer;
end;
var
MemCheckRec : PMemCheckRec;
AllocCol : PAllocCollection;
{****************************************************************************}
{* TAllocCollection *}
{****************************************************************************}
function TAllocCollection.Compare(Key1, Key2 : pointer) : integer;
begin
if longint(Key1) < longint(Key2)
then Compare := -1
else
if longint(Key1) = longint(Key2)
then Compare := 0
else Compare := 1;
end;
procedure TAllocCollection.FreeItem(Item : pointer);
begin
Dispose(PAllocItem(Item));
end;
procedure TAllocCollection.Insert(Item : pointer);
var
Index : integer;
l1,l2 : longint;
begin
if Search(KeyOf(Item), Index)
then begin
PrintError('Attempt to allocate memory at same address.', 0);
Halt(1);
end
else begin
AtInsert(Index, Item);
end;
end;
function TAllocCollection.KeyOf(Item : pointer) : pointer;
begin
KeyOf := PAllocItem(Item)^.MemPtr;
end;
{****************************************************************************}
{* MemCheckOn and Off *}
{****************************************************************************}
procedure MemCheckOn; assembler;
asm
les di,MemCheckRec
mov ax,1
mov es:[di].TMemCheckRec.CheckMem,ax
end;
procedure MemCheckOff; assembler;
asm
les di,MemCheckRec
xor ax,ax
mov es:[di].TMemCheckRec.CheckMem,ax
end;
{****************************************************************************}
{* StoreAlloc and FreeAlloc *}
{****************************************************************************}
procedure StoreAlloc(MemPtr : pointer; Size : word);
var
AllocItem : PAllocItem;
begin
{ turn MemChecking of to avoid recursive loops }
asm
les di,MemCheckRec
xor ax,ax
mov es:[di].TMemCheckRec.CheckMem,ax
end;
{ allocate memory tracking item }
New(AllocItem);
{ store data about current allocation in it }
asm
les di,AllocItem
mov bx,[bp]
ror bx,1
rol bx,1
jnc @@1
dec bx
@@1:
mov ax,word ptr ss:[bx+02h]
mov word ptr es:[di].TAllocItem.Caller,ax
mov ax,word ptr ss:[bx+04h]
mov word ptr es:[di].TAllocItem.Caller+2,ax
mov bx,ss:[bx]
ror bx,1
rol bx,1
jnc @@2
dec bx
@@2:
cmp word ptr ss:[bx],0
je @@end_of_stack
mov ax,word ptr ss:[bx+02h]
mov word ptr es:[di].TAllocItem.CallerItsCaller,ax
mov ax,word ptr ss:[bx+04h]
mov word ptr es:[di].TAllocItem.CallerItsCaller+2,ax
jmp @@3
@@end_of_stack:
xor ax,ax
mov word ptr es:[di].TAllocItem.CallerItsCaller,ax
mov word ptr es:[di].TAllocItem.CallerItsCaller+2,ax
@@3:
push ds
lds si,MemPtr
mov word ptr es:[di].TAllocItem.MemPtr,si
mov word ptr es:[di].TAllocItem.MemPtr+2,ds
pop ds
mov ax,Size
mov word ptr es:[di].TAllocItem.Size,ax
end;
{ insert allocation tracking item }
AllocCol^.Insert(AllocItem);
asm
{ turn MemChecking on }
les di,MemCheckRec
mov ax,1
mov es:[di].TMemCheckRec.CheckMem,ax
{ and restore ax and dx }
mov ax,word ptr &MemPtr
mov dx,word ptr &MemPtr+2
end;
end;
procedure FreeAlloc(MemPtr : pointer; Size : word);
function LowerMemoryCheck(Item : PAllocItem) : Boolean;
{* checks only first four bytes... *}
var
p : pointer;
begin
LowerMemoryCheck := FALSE;
with Item^ do begin
if Size <= 65536-8-16 then begin
if MemL[PtrRec(MemPtr).Seg:PtrRec(MemPtr).Ofs-4] <> $CCCCCCCC then
Exit;
end;
end; { of with }
LowerMemoryCheck := TRUE;
end;
function UpperMemoryCheck(Item : PAllocItem) : Boolean;
{* checks only first four bytes... *}
var
p : pointer;
begin
UpperMemoryCheck := FALSE;
with Item^ do begin
if Size <= 65536-8-8 then begin
if MemL[PtrRec(MemPtr).Seg:PtrRec(MemPtr).Ofs+Size] <> $CCCCCCCC then
Exit;
end;
end; { of with }
UpperMemoryCheck := TRUE;
end;
var
Index : integer;
begin
{ turn memory checking off }
asm
les di,MemCheckRec
xor ax,ax
mov es:[di].TMemCheckRec.CheckMem,ax
end;
with AllocCol^ do begin
if not Search(MemPtr, Index) then begin
PrintError('Attempt to dispose a non-allocated block.', 0);
MemCheckOn; { installed exit handlers might dispose here after }
Halt(1);
end;
if PAllocItem(At(Index))^.Size <> Size then begin
PrintError('Attempt to dispose a memory block with wrong block size. ' +
'Expected block size: ' + StrW(PAllocItem(At(Index))^.Size) +
'. Got: ' + StrW(Size), 0);
MemCheckOn; { installed exit handlers might dispose here after }
Halt(1);
end;
if not LowerMemoryCheck(PAllocItem(At(Index))) then begin
PrintError('Memory before allocated area corrupt!', 0);
MemCheckOn; { installed exit handlers might dispose here after }
Halt(1);
end;
if not UpperMemoryCheck(PAllocItem(At(Index))) then begin
PrintError('Memory after allocated area corrupt!', 0);
MemCheckOn; { installed exit handlers might dispose here after }
Halt(1);
end;
AtFree(Index);
end;
asm
{ turn MemChecking on }
les di,MemCheckRec
mov ax,1
mov es:[di].TMemCheckRec.CheckMem,ax
{ and restore ax, bx and cx }
mov ax,Size
mov cx,word ptr &MemPtr
mov bx,word ptr &MemPtr+2
end;
end;
procedure MemCheckReport;
const
CallerWidth = 70;
var
t : text;
Amount : longint;
procedure Print(Item : PAllocItem); far;
function GetAddress(Address : pointer) : string;
var
LogicalAddr : pointer;
LineNumber : PLineNumber;
Symbol : PSymbol;
s : string;
begin
LogicalAddr := GetLogicalAddr(Address);
if TDInfoPresent(nil)
then begin
New(LineNumber, AtAddr(LogicalAddr));
if LineNumber = nil
then begin
s := HexStr(PtrRec(LogicalAddr).Seg) + ':' + HexStr(PtrRec(LogicalAddr).Ofs);
end
else begin
s := LineNumber^.ItsCorrelation^.ItsSourceFile^.ItsName + ' (' + StrW(LineNumber^.Value) + ') ';
New(Symbol, AtAddr(LogicalAddr));
if Symbol <> nil then begin
if Symbol^.ItsType^.ReturnType = 1
then s := s + 'procedure '
else s := s + 'function ';
if Symbol^.ItsType^.ID = tid_SpecialFunc then begin
s := s + Symbol^.ItsType^.ItsClassType^.ItsName + '.';
end;
s := s + Symbol^.ItsName + ';';
Dispose(Symbol, Done);
end;
Dispose(LineNumber, Done);
end;
end
else
s := HexStr(PtrRec(LogicalAddr).Seg) + ':' + HexStr(PtrRec(LogicalAddr).Ofs);
GetAddress := s;
end;
begin
with Item^ do begin
writeln(t, LeftJustify(GetAddress(Caller), CallerWidth), ' ', Size:5);
writeln(t, ' ', LeftJustify(GetAddress(CallerItsCaller), CallerWidth-2));
Inc(Amount, Size);
end;
end;
const
BufSize = 1024;
var
Buffer : array[1..BufSize] of char;
begin
MemCheckOff;
Assign(t, ReportFileName);
Rewrite(t);
SetTextBuf(t, Buffer, BufSize);
writeln(t, 'Not disposed memory report. Date: ', GetDateStr, ' Time: ', GetTimeStr);
writeln(t);
writeln(t, LeftJustify('Caller', CallerWidth), ' Size');
writeln(t);
Amount := 0;
AllocCol^.ForEach(@Print);
writeln(t);
writeln(t);
writeln(t, 'Total not disposed memory: ', Amount, ' bytes');
writeln(t, 'Total items: ', AllocCol^.Count);
Close(t);
MemCheckOn;
end;
begin
MemCheckRec := ErrorAddr;
if MemCheckRec <> nil then begin
AllocCol := New(PAllocCollection, Init(4096,4096));
MemCheckRec^.StoreAlloc := @StoreAlloc;
MemCheckRec^.FreeAlloc := @FreeAlloc;
MemCheckOn;
end;
end. { of unit MemCheck }